2.4 Ausdrücke, Operanden und Operatoren 

Beginnen wir mit mathematischen Ausdrücken, um dann die Schreibweise in Java zu ermitteln. Eine mathematische Formel, etwa der Ausdruck -27 * 9, besteht aus Operanden (engl. operand) und Operatoren (engl. operator). Ein Operand ist eine Variable oder ein Literal. Im Fall einer Variablen wird der Wert aus der Variablen ausgelesen und mit ihm die Berechnung durchgeführt.
Beispiel Ein Ausdruck mit Zuweisungen:
int i = 12, j; j = i * 2; |
Die Multiplikation berechnet das Produkt von 12 und 2 und speichert das Ergebnis in j ab. Von allen primitiven Variablen, die in dem Ausdruck vorkommen, wird also der Wert ausgelesen und in den Ausdruck eingesetzt. [Es gibt Programmiersprachen, in denen werden Wertoperationen besonders gekennzeichnet. So etwa in LOGO. Eine Wertoperation schreibt sich dort mit einem Doppelpunkt vor der Variablen, etwa :X + :Y. ]
Dies nennt sich auch Wertoperation, da der Wert der Variablen betrachtet wird und nicht ihr Speicherort oder gar ihr Variablenname.
Die Arten von Operatoren
Operatoren verknüpfen die Operanden. Ist ein Operator auf genau einem Operand definiert, so nennt er sich unärer Operator (oder einstelliger Operator). Das Minus (negatives Vorzeichen) vor einem Operand ist ein unärer Operator, da er für genau den folgenden Operanden gilt. Die üblichen Operatoren Plus, Minus, Mal und Division sind binäre (zweistellige) Operatoren. Es gibt auch einen Fragezeichenoperator für bedingte Ausdrücke, der dreistellig ist.
Ausdrücke
Ein Ausdruck (engl. expression) ergibt bei der Auswertung ein Ergebnis. Dieser Wert wird auch Resultat genannt. Ausdrücke haben immer einen Wert, während das für Anweisungen (wie eine Schleife) nicht gilt. Daher kann ein Ausdruck an allen Stellen stehen, an denen ein Wert benötigt wird. Dieser Wert ist entweder ein
Operatoren erlauben die Verbindung einzelner Ausdrücke zu neuen Ausdrücken. Einige Operatoren sind aus der Schule bekannt, wie Addition, Vergleich, Zuweisung und Weitere. C(++)-Programmierer werden viele Freunde wiedererkennen.
2.4.1 Zuweisungsoperator 

Der Zuweisungsoperator kopiert den Wert eines Ausdrucks der rechten Seite in die Variable der linken Seite. In Java stellt das Gleichheitszeichen = eine Zuweisung (engl. assignment) mit dar. Der linke Teil einer Zuweisung ist eine Variable, die den Wert speichert, und nennt sich l-Wert. Der rechte Teil ist der Ausdruck, der ausgewertet in die Variable geschrieben wird. Er nennt sich r-Wert.
int a; a = 12 * 3;
Die Zuweisungen sehen zwar so aus wie mathematische Gleichungen, doch existiert ein wichtiger Unterschied: Die Formel a = a + 1 ist – zumindest im Dezimalsystem ohne zusätzliche Algebra – mathematisch nicht zu erfüllen, da es kein a geben kann, das a = a + 1 erfüllt. Aus Programmiersicht ist es in Ordnung, da die Variable a um eins erhöht wird. In anderen Programmiersprachen wird die Zuweisung durch ein anderes Symbol deutlich gemacht (etwa :=); Java folgt hier der Tradition von C(++) und definiert einen binären Vergleichsoperator mit ==.
Mehrere Zuweisungen in einer Anweisung
Zuweisungen der Form a = b = 0 sind erlaubt und gleichbedeutend mit zwei Anweisungen b = 0 und a = b beziehungsweise mit expliziter Klammerung a = (b = 0). Daran lässt sich ablesen, dass beim Zuweisungsoperator die Auswertung von rechts nach links erfolgt. Doch wenn wir meinen, dass
a = (b = c + d) + e;
eine Vereinfachung im Vergleich zu
b = c + d; a = b + e;
ist, sollten wir mit einer Zuweisung pro Zeile auskommen.
2.4.2 Arithmetische Operatoren 

Ein arithmetischer Operator verknüpft die Operanden mit den Operatoren Addition (+), Subtraktion (-), Multiplikation (*) und Division (/). Zusätzlich gibt es den Restwert-Operator %, der den bei der Division verbleibenden Rest betrachtet. Alle Operatoren sind für ganzzahlige Werte sowie für Fließkommazahlen definiert. [In C sind sie nur für Ganzzahlen definiert. ] Die arithmetischen Operatoren sind binär, und auf der linken und rechten Seite sind die Typen numerisch. Der Ergebnistyp ist ebenfalls numerisch.
Numerische Umwandlung
Bei unterschiedlichen Datentypgrößen bringt der Compiler vor der Anwendung der Operation alle Operanden auf den größten vorkommenden Typ. Das nennt sich numerische Umwandlung (engl. numeric promotion). Bei byte und short gilt die Sonderregelung, dass sie vorher auf int konvertiert werden. [http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#26917 ] (Auch im Java Bytecode gibt es keine arithmetischen Operationen auf byte, short und char.) Anschließend wird die Operation ausgeführt, und der Ergebnistyp entspricht dem umfassenderen Typ.
Der Divisionsoperator
Der binäre Operator »/« bildet den Quotienten aus Dividend und Divisor. Auf der linken Seite steht der Dividend und auf der rechten der Divisor. Die Division ist für Ganzzahlen und für Fließkommazahlen definiert. Bei der Ganzzahldivision wird zu null hin gerundet und das Ergebnis ist keine Fließkommazahl, so dass 1/3 das Ergebnis 0 ergibt, und nicht 0,333. Den Datentyp des Ergebnisses bestimmen die Operanden und nicht der Operator. Soll das Ergebnis vom Typ double sein, muss ein Operand ebenfalls double sein.
System.out.println( 1.0 / 3 ); // 0.3333333333333333 System.out.println( 1 / 3.0 ); // 0.3333333333333333 System.out.println( 1 / 3 ); // 0
Schon in der Schulmathematik war die Division durch null nicht definiert. Führen wir eine Ganzzahldivision mit dem Divisor 0 durch, so bestraft uns Java mit einer ArithmeticException. Bei Fließkommazahlen verläuft dies anders. Eine Division durch 0 liefert +/– unendlich und ein NaN bei 0.0/0.0. Ein NaN steht für Not-A-Number und wird vom Prozessor erzeugt, falls er eine mathematische Operation wie die Division durch null nicht durchführen kann. In Kapitel 5 werden wir auf NaN noch einmal zurückkommen.
Der Restwert-Operator %
Eine Ganzzahldivision muss nicht unbedingt glatt aufgehen, wie im Fall 9/2. In diesem Fall gibt es den Rest 1. Diesen Rest liefert der Restwert-Operator (engl. remainder operator), oft auch Modulo genannt. (Mathematiker unterscheiden die beiden Begriffe Rest und Modulo, da ein Modulo nicht negativ ist, der Rest in Java aber schon. Das soll uns aber egal sein.)
System.out.println( 9% 2 ); // 1
Im Gegensatz zu C(++) [Wir müssten in C(++) die Funktion fmod() benutzen. ] ist der Restwert-Operator in Java auch auf Fließkommazahlen anwendbar, und die Operanden können negativ sein. [Die Sprachdefinition von C(++) schreibt bei der Division und beim Modulo mit negativen Zahlen keine Berechnungsmethode vor. ]
System.out.println( 12. % 2.5 ); // 2.0
Die Division und der Restwert richten sich in Java nach einer einfachen Formel: int(a/b)*b + (a%b) = a.
| Beispiel Die Gleichung ist erfüllt, wenn wir etwa a = 10 und b = 3 wählen. Es gilt: int(10/3) = 3 und 10 % 3 ergibt 1. Dann ergeben 3 * 3 + 1 = 10. |
Aus dieser Gleichung folgt, dass beim Restwert das Ergebnis nur dann negativ ist, wenn der Dividend negativ ist; er ist nur dann positiv, wenn der Dividend positiv ist. Es ist leicht einzusehen, dass das Ergebnis der Restwert-Operation immer echt kleiner ist als der Wert des Divisors. Wir haben den gleichen Fall wie bei der Ganzzahldivision, dass ein Divisor mit dem Wert 0 eine ArithmeticException auslöst und bei Fließkommazahlen zum Ergebnis NaN führt.
| Beispiel Unterschiedliche Vorzeichen beim Restwert-Operator:
Listing 2.6 RemainerAndDivDemo.java, main() System.out.println( " 5% 3 = " + ( 5% 3) ); // 2 System.out.println( " 5 / 3 = " + ( 5 / 3) ); // 1 System.out.println( " 5% -3 = " + ( 5% -3) ); // 2 System.out.println( " 5 / -3 = " + ( 5 / -3) ); // -1 System.out.println( "-5% 3 = " + (-5% 3) ); // -2 System.out.println( "-5 / 3 = " + (-5 / 3) ); // -1 System.out.println( "-5% -3 = " + (-5% -3) ); // -2 System.out.println( "-5 / -3 = " + (-5 / -3) ); // 1 Gewöhnungsbedürftig ist die Tatsache, dass der zweite Operand das Vorzeichen des Restes definiert und niemals der erste. |
Restwert für Fließkommazahlen und IEEEremainder()
Über die oben genannte Formel können wir auch bei Fließkommazahlen das Ergebnis einer Restwert-Operation leicht berechnen. Dabei muss beachtet werden, dass sich der Operator nicht so wie unter IEEE 754 verhält. Denn diese Norm schreibt vor, dass die Restwert-Operation den Rest von einer rundenden Division berechnet und nicht von einer abschneidenden. So wäre das Verhalten nicht analog zum Restwert bei Ganzzahlen. Java definiert den Restwert jedoch bei Fließkommazahlen genauso wie den Restwert bei Ganzzahlen. Wünschen wir ein Restwert-Verhalten, wie es IEEE 754 vorschreibt, so können wir immer noch die Bibliotheksfunktion Math.IEEEremainder() verwenden.
Auch bei der Restwert-Operation bei Fließkommazahlen werden wir niemals eine Exception erwarten. Eventuelle Fehler werden, wie im IEEE-Standard beschrieben, mit NaN angegeben. Ein Überlauf oder Unterlauf kann zwar passieren, aber nicht geprüft werden.
Rundungsfehler
Prinzipiell sollten Anweisungen wie 1.1 – 0.1 immer 1.0 ergeben, jedoch treten interne Rundungsfehler bei der Darstellung auf und lassen das Ergebnis von Berechnung zu Berechnung immer ungenauer werden. Ein besonders ungünstiger Fehler trat 1994 beim Pentium-Prozessor im Divisionsalgorithmus Radix-4 SRT auf, ohne dass der Programmierer der Schuldige war.
double x, y, z; x = 4195835.0; y = 3145727.0; z = x - (x/y) * y; System.out.println( z );
Ein fehlerhafter Prozessor liefert hier 256, obwohl laut Rechenregel das Ergebnis 0 sein muss. Laut Intel sollte für einen normalen Benutzer (Spieler, Softwareentwickler, Surfer?) der Fehler nur alle 27 000 Jahre auftauchen. Glück für die meisten. Eine Studie von IBM errechnete eine Fehlerhäufigkeit von einmal in 24 Tagen. Alles in allem hat Intel die CPUs zurückgenommen, über 400 Millionen US-Dollar verloren und spät den Kopf gerade noch aus der Schlinge gezogen.
Die meisten Rundungsfehler resultieren aber daher, dass endliche Dezimalbrüche im Rechner als Näherungswerte für periodische Binärbrüche repräsentiert werden müssen. 0,1 entspricht einer periodischen Mantisse im IEEE-Format.
2.4.3 Unäres Minus und Plus 

Die binären Operatoren sitzen zwischen zwei Operanden, während sich ein unärer Operator genau einen Operanden vornimmt. Das unäre Minus (Operator zur Vorzeichenumkehr) etwa dreht das Vorzeichen des Operanden um. So wird aus einem positiven Wert ein negativer und aus einem negativen ein positiver.
Beispiel Drehe das Vorzeichen einer Zahl um:
a = -a; Alternativ ist: |
Das unäre Plus ist eigentlich unnötig; die Entwickler haben es jedoch aus Symmetriegründen mit eingeführt.
Beispiel Minus und Plus sitzen direkt vor dem Operanden, und der Compiler weiß selbstständig, ob dies unär oder binär ist. Der Compiler erkennt auch folgende Konstruktion:
int i = - - - 2 + - + 3; Dies ergibt den Wert -5. Einen Ausdruck wie ---2+-+3 erkennt der Compiler dagegen nicht an, da die zusammenhängenden Minuszeichen als Inkrement interpretiert werden und nicht als unärer Operator. Das Leerzeichen ist also bedeutend. |
Vorzeichen erfragen
Um für das Vorzeichen einen Wert +1 für positive oder –1 für negative Zahlen und 0 für 0 zu bekommen, lässt sich die Funktion signum() verwenden. Sie ist nicht ganz logisch auf die Klasse Math für Fließkommazahlen und Integer/Long für Ganzzahlen verteilt:
- java.lang.Integer.signum( int i )
- java.lang.Long.signum( long i )
- java.lang.Math.signum( float f )
2.4.4 Zuweisung mit Operation 

In Java lassen sich Zuweisungen mit numerischen Operatoren kombinieren. Für einen Operator # im Ausdruck a = a # (b) kürzt der Verbundoperator den Ausdruck zu a #= b ab. So addiert a += 2 zur Variable a 2 hinzu, und der Rückgabewert ist die um 2 erhöhte Variable a.
Besondere Obacht sollten wir auf die automatische Klammerung geben. Bei einem Ausdruck wie a *= 3 + 5 gilt a = a * (3 + 5) und nicht selbstverständliche die Punkt-vor-Strich-Regelung a = a * 3 + 5.
Falls es sich bei der rechten Seite um einen komplexeren Ausdruck handelt, wird dieser nur einmal ausgewertet. Dies ist wichtig bei Methodenaufrufen, die Nebenwirkungen besitzen, etwa Zustände wie einen Zähler verändern.
Typanpassung beim Verbundoperator
Beim Verbundoperator wird noch etwas mehr gemacht, als E1 #= E2 zu E1 = (E1) # (E2) aufzulösen. Interessanterweise kommt auch noch der Typ von E1 ins Spiel, denn der Ausdruck E1 # E2 wird vor der Zuweisung auf den Datentyp von E1 gebracht, sodass es genau heißen muss: E1 #= E2 wird zu E1 = (Typ von E1)((E1) # (E2)).
2.4.5 Präfix- oder Postfix-Inkrement und -Dekrement 

Das Herauf- und Heruntersetzen von Variablen ist eine sehr häufige Operation, wofür die Entwickler in der Vorgängersprache C auch einen Operator spendiert hatten. Die praktischen Operatoren ++ und -- kürzen die Programmzeilen zum Inkrement und Dekrement ab.
i++; // Abkürzung für i = i + 1 j--; // j = j - 1
Eine lokale Variable muss allerdings vorher initialisiert sein, da ein Lesezugriff vor einem Schreibzugriff stattfindet.
Vorher oder nachher
Die beiden Operatoren liefern einen Ausdruck und geben daher einen Wert zurück. Es macht jedoch einen feinen Unterschied, wo dieser Operator platziert wird. Es gibt ihn nämlich in zwei Varianten: vor der Variable und dahinter. Steht das Inkrement vor der Variable, sprechen wir von Prä-Inkrement/Prä-Dekrement, steht es dahinter, von Post-Inkrement/Post-Dekrement – kurz auch Präfix/Postfix genannt. Nutzen wir einen Präfix-Operator, so wird die Variable erst herauf- beziehungsweise heruntergesetzt und dann der Wert geliefert. Neben der Wertrückgabe gibt es eine Veränderung der Variable.
Beispiel Präfix/Postfix in einer Ausgabeanweisung:
int i = 10, j = 20; System.out.println( ++i ); // 11 System.out.println( --j ); // 19 System.out.println( i ); // 11 System.out.println( j ); // 19 Die Variable wird erst heraufgesetzt und dann ausgegeben. Wiederholen wir mit i und j die Ausgaben mit Postfix: int i = 10, j = 20; System.out.println( i++ ); // 10 System.out.println( j-- ); // 20 System.out.println( i ); // 11 System.out.println( j ); // 19 Der Wert wird im Ausdruck verwendet und erst anschließend heraufgesetzt. Wir bekommen mit dem Präfix den Ausdruck nach der Operation und mit dem Postfix den Ausdruck davor. |
Das Post-Inkrement finden wir auch im Namen der Programmiersprache C++. Es soll ausdrücken, dass es C-mit-eins-drauf ist, also ein verbessertes C. Mit dem Wissen über den Postfix-Operator ist klar, dass wir erst einen Zugriff haben und dann die Erhöhung stattfindet – also C++ ist auch nur C, und der Vorteil kommt später. (Einer der Entwickler von Java, Bill Joy, hat einmal Java als C++-- [--C++ könnte besser passen: Erst wird C++ bereinigt und dann zu Java erweitert. ] beschrieben. Er meinte damit C++ ohne die schwer zu pflegenden Eigenschaften.) Bei den von Microsoft geschaffenen Buchstabe-#-Sprachen – das # liest sich »Sharp« – wurde die Idee von den Musiknoten entlehnt. Ein Sharp hinter einer Note bedeutet, dass sie um einen Halbton erhöht wird. Das stimmt: Insbesondere C# nähert sich nur in Halbtonschritten.
| Beispiel In Java sind Inkrement (++) und Dekrement (--) für alle numerischen Datentypen erlaubt, also auch für Fließkommazahlen. |
Einige Kuriositäten
Wir wollen uns abschließend noch mit einer Besonderheit des Post-Inkrements und Prä-Inkrements beschäftigen, die nicht nachahmenswert ist:
a = 2; a = ++a; // a = 3 b = 2; b = b++; // b = 2
Im ersten Fall bekommen wir den Wert 3 und im zweiten Fall 2. Der erste Fall überrascht nicht. Denn a = ++a erhöht den Wert 2 um 1, und anschließend wird 3 der Variablen a zugewiesen. Bei b ist es raffinierter. Der Wert von b ist 2, und dieser Wert wird intern vermerkt. Anschließend erhöht b++ die Variable b. Doch die Zuweisung setzt b auf den gemerkten Wert, der 2 war. Also ist b = 2.
2.4.6 Die relationalen Operatoren und die Gleichheitsoperatoren 

Relationale Operatoren sind Vergleichsoperatoren, die Ausdrücke miteinander vergleichen und einen Wahrheitswert vom Typ boolean zurückgeben. Die von Java zur Verfügung gestellten Operatoren sind:
- Größer (>)
- Größer-gleich (>=), Kleiner-gleich (<=)
Zudem kommen zwei Vergleichsoperatoren hinzu, die Java als Gleichheitsoperatoren bezeichnet:
- Test auf Gleichheit (==)
Dass Java hier einen Unterschied macht, liegt an einem etwas anderen Vorrang, der uns aber nicht weiter beschäftigen soll.
Ebenso wie arithmetische Operatoren passen die relationalen Operatoren ihre Operanden an einen gemeinsamen Typ an. Handelt es sich bei den Typen um Referenztypen, so sind nur die Vergleichsoperatoren == und != erlaubt.
Kaum Verwechslungsprobleme durch == und =
Die Verwendung des relationalen Operators == und der Zuweisung = führt bei Einsteigern oft zu Problemen, da die Mathematik für Vergleiche und Zuweisungen immer nur ein Gleichheitszeichen kennt. Glücklicherweise ist das Problem in Java nicht so drastisch wie beispielsweise in C(++), da die Typen der Operatoren unterschiedlich sind. Der Vergleichsoperator ergibt immer nur den Rückgabewert boolean. Zuweisungen von numerischen Typen ergeben jedoch wieder einen numerischen Typ. Es kann also kein Problem wie das folgende geben:
int a = 10, b = 11; boolean result1 = ( a = b ); // Compilerfehler boolean result2 = ( a == b ); // Das ist OK
Beispiel Die Wahrheitsvariable hatVorzeichen soll dann true sein, wenn das Zeichen vorzeichen gleich dem Minus ist.
boolean hatVorzeichen = (vorzeichen == '-'); Schön ist die Auswertungsreihenfolge zu sehen: Erst wird das Ergebnis des Vergleichs berechnet, und dieser Wahrheitswert wird anschließend in hatVorzeichen kopiert. |
2.4.7 Logische Operatoren Und, Oder, Xor, Nicht 

Mit logischen Operatoren werden Wahrheitswerte nach definierten Mustern verknüpft. Logische Operatoren operieren nur auf boolean-Typen, andere Typen führen zu Compilerfehlern. Java bietet die Operatoren Und (&&), Oder (||), Xor (^) und Nicht (!) an. Xor ist eine Operation, die genau dann falsch zurückgibt, wenn entweder beide Operatoren wahr oder beide falsch sind. Sind sie unterschiedlich, so ist das Ergebnis wahr. Xor heißt auch exklusives beziehungsweise ausschließendes Oder.
Kurzschlussoperatoren
Eine Besonderheit sind die beiden Operatoren && (Und) beziehungsweise || (Oder). In der Regel muss ein logischer Ausdruck nur dann weiter ausgewertet werden, wenn er das Schlussergebnis noch beeinflussen kann. Zwei Operatoren bieten sich zur Optimierung der Ausdrücke an:
- Und: Ist einer der beiden Ausdrücke falsch, so kann der Ausdruck schon nicht mehr wahr werden. Das Ergebnis ist falsch.
- Oder: Ist mindestens einer der Ausdrücke schon wahr, so ist auch der gesamte Ausdruck wahr.
Der Compiler bzw. die Laufzeitumgeung kann den Programme abkürzen. Daher nennen sich die beiden Operatoren auch Kurzschlussoperatoren (engl. short-circuit-operator). [Den Begriff verwendet die Java Sprachdefinition nicht! Sie dazu auch http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.23. ]
Nicht-Kurzschlussoperatoren
In einigen Fällen ist erwünscht, dass die Laufzeitumgebung alle Teilausdrücke auswertet. Das kann der Fall sein, wenn Funktionen Nebenwirkungen haben sollen, etwa Zustände ändern. Daher bietet Java zusätzliche die nicht über einen Kurzschluss arbeitenden Operatoren | und & an, die eine Auswertung aller Teilausdrücke erzwingen.
Beispiel In dem ersten Ausdruck wird die Methode foo() nicht aufgerufen, im zweiten schon.
boolean b1 = true || foo(); // foo() wird nicht aufgerufen boolean b2 = false & foo(); // foo() wird aufgerufen |
Für Xor kann es keinen Kurzschlussoperator geben, da immer beide Operanden ausgewertet werden müssen, bevor das Ergebnis feststeht.
2.4.8 Rang der Operatoren in der Auswertungsreihenfolge 

Aus der Schule ist der Spruch »Punktrechnung geht vor Strichrechnung« bekannt, sodass sich der Ausdruck 1 + 2 * 3 zu 7 und nicht zu 9 auswertet. [Dass von diesen Rechnungen eine gewisse Spannung ausgeht, zeigen diverse Fernsehkanäle, die damit ihr Abendprogramm füllen. ]
Beispiel Zur Umwandlung einer Temperatur von Fahrenheit in Celsius zieht man von dem Wert in Fahrenheit 32 ab und multipliziert das Ergebnis mit 5/9:
celsius = fahrenheit - 32 * 5 / 9; Die Umsetzung ist falsch, denn die Laufzeitumgebung zieht 32 * 5/9, also 17, von Fahrenheit ab, was keine gültige Umrechnung ist. Richtig ist Folgendes: celsius = ( fahrenheit - 32 ) * 5 / 9; |
In den meisten Programmiersprachen gibt es eine Unzahl von Operatoren neben Plus und Mal, die alle ihre eigenen Vorrangregeln besitzen. [Es gibt Programmiersprachen wie APL, die keine Vorrangregeln kennen. Sie werten die Ausdrücke streng von rechts nach links oder umgekehrt aus. ] Der Multiplikationsoperator besitzt zum Beispiel eine höhere Priorität und damit eine andere Auswertungsreihenfolge als der Plus-Operator.
Die Rechenregeln für Mal vor Plus kann sich jeder noch leicht merken. Auch ist leicht zu merken, dass die typischen arithmetischen Operatoren wie Plus und Mal eine höhere Priorität als Vergleichsoperationen haben. Komplizierter ist die Auswertung bei den zahlreichen Operatoren, die seltener im Programm vorkommen.
Beispiel Wie ist die Auswertung bei dem nächsten Ausdruck?
boolean A = false, B = false, C = true; System.out.println( A && B || C ); |
Gilt, dass entweder A && B oder C wahr sein müssen oder etwa A und B || C? Das Ergebnis fällt unterschiedlich aus. Entweder ist es true oder false.
Für derlei Feinheiten gibt es zwei Lösungen: entweder in einer Tabelle mit Vorrangregeln nachschlagen – §15.7 der JLS – oder auf diese Ungenauigkeiten verzichten.
Die Tabelle lehrt uns, dass im Beispiel A && B || C das Und stärker als das Oder bindet, also der Wert mit der Belegung A=false, B=false, C=true zu true ausgewertet wird. Vermutlich gibt es Programmierer, die dies wissen oder eine Tabelle mit Rangordnungen immer am Monitor kleben haben. Aber beim Durchlesen von fremdem Code ist es nicht schön, immer wieder die Tabelle konsultieren zu müssen, die verrät, ob nun das binäre Xor oder das binäre Und stärker bindet.
| Tipp Alle Ausdrücke, die über die einfache Regel »Punktrechnung geht vor Strichrechnung« hinausgehen, sollten geklammert werden. Da die unären Operatoren ebenfalls sehr stark binden, kann eine Klammerung wegfallen. |
Die mathematische Assoziativität gilt bei Gleitkommazahlen natürlich nicht, da diese nicht ohne Rechenfehler ablaufen. Daher gilt eine Auswertung von links nach rechts.
2.4.9 Überladenes Plus für Strings 

Obwohl sich in Java die Operatoren fast alle auf primitive Datentypen beziehen, gibt es doch eine bemerkenswerte Verwendung des Plus-Operators. Diese wurde in Java eingeführt, da ein Aneinanderhängen von Zeichenketten oft benötigt wird. Objekte vom Typ String können durch den Plus-Operator mit anderen Strings und Datentypen verbunden werden. Falls zusammenhängende Teile nicht alle den Datentyp String annehmen, werden sie automatisch in einen String umgewandelt.
"Sandmännchen wünscht " + 12
ergibt den einen String »Sandmännchen wünscht 12«.
Besteht der Ausdruck aus mehreren Teilen, so muss die Auswertungsreihenfolge beachtet werden, andernfalls kommt es zu seltsamen Zusammensetzungen. So ergibt "Aufruf von " + 1 + 0 + 0 + " Ökonomen" tatsächlich »Aufruf von 100 Ökonomen« und nicht »Aufruf von 1 Ökonomen«, da der Compiler die Konvertierung in Strings dann startet, wenn der den Ausdruck als String-Objekt erkannt hat.
| Beispiel Auswertungsreihenfolge vom Plus:
Listing 2.7 PlusString.java, main() System.out.println( 1 + 2 ); // 3 System.out.println( "1" + 2 ); // 12 System.out.println( 1 + "2" ); // 12 System.out.println( 1 + "2" + 3 ); // 123 System.out.println( "1" + 2 + 3 ); // 123 System.out.println( 1 + 2 + "3" ); // 33 System.out.println( '1' + 2 ); // 51 (ASCII-Wert von '1' ist 49) System.out.println( 1 + '2' ); // 51 |
Beispiel Der Plus-Operator für Zeichenketten geht streng von links nach rechts und bereitet mit eingebetteten arithmetischen Ausdrücken mitunter Probleme. Eine Klammerung hilft, wie im Folgenden zu sehen ist:
"Ist 1 größer als 2? " + (1 > 2 ? "nein" : "ja"); Wäre der Ausdruck um den Bedingungsoperator nicht geklammert, dann würde der Plus-Operator an die Zeichenkette die 1 anhängen und es käme der >-Operator. Der erwartet aber kompatible Datentypen, die in unserem Fall – links stünde die Zeichenkette und rechts ein int – nicht gegeben sind. |
2.4.10 Was C(++)-Programmierer vermissen könnten 

Da es in Java keine Pointer-Operationen gibt, existiert das Operatorzeichen zur Referenzierung (&) und Dereferenzierung (*) nicht. Ebenso ist ein sizeof unnötig, da das Laufzeitsystem und der Compiler immer die Größe von Klassen kennen beziehungsweise die primitiven Datentypen immer eine feste Länge haben. Eine abgeschwächte Version vom Kommaoperator ist in Java nur im Kopf von for-Schleifen erlaubt.



